package com.briup.gateway.routers;

import cn.hutool.json.JSONUtil;
import com.alibaba.cloud.nacos.NacosConfigManager;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

import javax.annotation.PostConstruct;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Executor;

@Slf4j
@Component
@RequiredArgsConstructor
public class DynamicRouteLoader {

    //用于获取 nacosConfigService对象（具有拉取配置、添加监听器等功能）
    private final NacosConfigManager nacosConfigManager;

    //专门用来 操作路由信息表(save delete)
    private final RouteDefinitionWriter writer;

    private final String dataId = "gateway-routes.json";
    private final String group = "DEFAULT_GROUP";

    //记录所有路由id
    private final Set<String> routeIds = new HashSet<>();

    // @PostConstruct：创建当前类对象后，立马执行该方法
    @PostConstruct
    public void initRouteConfigListener() throws NacosException {
        //1.项目启动，先去拉取nacos配置，同时添加 配置监听器
        String configInfo = nacosConfigManager.getConfigService()
                .getConfigAndSignListener(dataId, group, 5000, new Listener() {
                    @Override
                    public Executor getExecutor() {
                        //此处逻辑简单，不需要 线程池
                        return null;
                    }

                    @Override
                    public void receiveConfigInfo(String configInfo) {
                        //2.一旦监听到配置变更，立即更新路由表
                        updateConfigInfo(configInfo);
                    }
                });
        //3.第一次读取配置，需要更新到路由表
        updateConfigInfo(configInfo);
    }

    //更新路由表
    public void updateConfigInfo(String configInfo) {
        log.debug("监听到路由表配置信息：{}", configInfo);

        //1.解析配置信息(json文件)，获取RouteDefinition对象()
        List<RouteDefinition> routeDefinitions =
                JSONUtil.toList(configInfo, RouteDefinition.class);

        //2.删除旧的路由表
        for(String routeId : routeIds) {
            writer.delete(Mono.just(routeId)).subscribe();
        }
        //清空路由表id集合
        routeIds.clear();

        //3.借助writer对象更新 路由信息表
        for(RouteDefinition routeDefinition : routeDefinitions) {
            // 3.1 更新路由表                 subscribe()订阅,必须调用
            writer.save(Mono.just(routeDefinition)).subscribe();
            // 3.2 记录路由id，便于下一次更新时删除
            routeIds.add(routeDefinition.getId());
        }
    }
}
